package top.codedance.iotp.client.net;

import cd.io.netty.buffer.PooledByteBufAllocator;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.client.net.handler.*;
import top.codedance.iotp.client.net.listenner.ConnectionListener;
import top.codedance.iotp.common.entity.NetworkEntity;
import top.codedance.iotp.common.handler.AbsractIdleHandler;
import top.codedance.iotp.common.protocol.CommonDecoderCodec;
import cd.io.netty.bootstrap.Bootstrap;
import cd.io.netty.channel.*;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.SocketChannel;
import cd.io.netty.channel.socket.nio.NioSocketChannel;
import cd.io.netty.handler.logging.LogLevel;
import cd.io.netty.handler.logging.LoggingHandler;
import cd.io.netty.handler.timeout.IdleStateHandler;
import org.apache.log4j.Logger;

import java.util.Arrays;

public class Client {
    private Logger logger = ClientApplication.getLogger(Client.class);

    private String host;
    private int port;

    public Client(String host, int port){
        if(ClientApplication.enableLoadBlance){
            NetworkEntity networkEntity = ClientApplication.loadBlance();
            if(networkEntity != null) {
                this.host = networkEntity.getIp();
                this.port = networkEntity.getPort();
            } else {
                ClientApplication.syslogInfo("Load blance fail, please turn off load balancing and try again or contact the administrator");
                System.exit(1);
            }
        }else {
            this.host = host;
            this.port = port;
        }
    }

    private EventLoopGroup group = new NioEventLoopGroup(ClientApplication.ioThreads);

    public void run() {
        createBootstrap(new Bootstrap());
    }

    public void createBootstrap(Bootstrap bootstrap){
        try {
            Bootstrap group = bootstrap.group(this.group);
            String logLevelArg = ClientApplication.commandCli.getArg("logLevel", "OFF");
            if(Arrays.asList(LogLevel.values(), String.class).contains(logLevelArg)) {
                group.handler(new LoggingHandler(LogLevel.valueOf(logLevelArg)));
            }
            group.channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .option(ChannelOption.SO_RCVBUF, 128 * 1024)
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("commonDecoderCodec", new CommonDecoderCodec(ClientApplication.getLogger(Client.class)))
                            .addLast("idleStateHandler", new IdleStateHandler(0, 0, 5))
                            .addLast(new IdleChannelHandler(Client.this, ClientApplication.getLogger(AbsractIdleHandler.class)))
                            .addLast(new RSAEncryptChannelHandler())
                            .addLast(new PluginsChannelHandler())
                            ;
                        }
                    });

            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            if (channelFuture.isSuccess()) {
                ClientApplication.channel = channelFuture.channel();
                ClientApplication.syslogInfo(String.format("Client connect server(%s:%s) success", host, port));
            }
            channelFuture.addListener(new ConnectionListener(Client.this));

            channelFuture.channel().closeFuture().sync();
            ClientApplication.syslogInfo("Client close success");
        } catch (InterruptedException e) {
            logger.debug(e.getMessage());
        } finally {
            group.shutdownGracefully();
        }
    }
}
